import {
  Component,
  ViewChild,
  Input,
  ViewEncapsulation,
  Output,
  EventEmitter,
  ChangeDetectionStrategy,
  ElementRef,
  ChangeDetectorRef,
  forwardRef,
  TemplateRef,
  Renderer2,
  OnInit,
  OnChanges,
  SimpleChanges,
  HostListener,
  TrackByFunction,
} from '@angular/core';
import { NG_VALUE_ACCESSOR, ControlValueAccessor } from '@angular/forms';
import { isNil as _isNil } from 'lodash';
import { AutocompleteDirective } from '../autocomplete/api';
import { ResizeObserverService } from '../core/services/resize-observer.service';
import { doFnResult, InputBoolean, InputNumber, typeConvert } from '../core/utils/api';
import { MultiAutocompleteItem, TagItem, Callback } from './interface';

@Component({
  selector: 'lv-multi-autocomplete',
  exportAs: 'lvMultiAutocomplete',
  templateUrl: './multi-autocomplete.component.html',
  changeDetection: ChangeDetectionStrategy.OnPush,
  encapsulation: ViewEncapsulation.None,
  providers: [
    {
      provide: NG_VALUE_ACCESSOR,
      useExisting: forwardRef((): typeof MultiAutocompleteComponent => MultiAutocompleteComponent),
      multi: true,
    },
  ],
  host: {
    '[class.lv-multi-autocomplete-host]': 'true',
    '[class.lv-multi-autocomplete-disabled]': 'lvDisabled',
    '[class.lv-multi-autocomplete-resize]': 'lvResize',
    '(click)': '!lvDisabled && _focusInput()',
    '(focusin)': '!lvDisabled && _changeClass(true)',
    '(focusout)': '!lvDisabled && _changeClass(false)',
  },
})
export class MultiAutocompleteComponent implements ControlValueAccessor, OnInit, OnChanges {
  @Input() lvPanelClass: string;
  @Input() lvPlaceholder: string; // 如果没有选择任何值，显示的占位符
  @Input() lvLoading: boolean = false;
  @Input() lvShowNoData: boolean = true;
  @Input() lvPanelWidth: string | number;
  @Input() lvBeforeValueChange: Callback;
  @Input() lvAllowNewItem: boolean = false;
  @Input() lvTrackBy: TrackByFunction<any>; // 内容列表trackBy
  @Input() lvTagTemplate: TemplateRef<void>;
  @Input() @InputBoolean() lvResize = false;
  @Input() @InputNumber() lvVirtualItemSize; // 每行高度
  @Input() lvLabelTemplate: TemplateRef<void>;
  @Input() @InputBoolean() lvDisabled = false;
  @Input() @InputNumber() lvMaxLength: number;
  @Input() lvNoDataTemplate: TemplateRef<void>;
  @Input() lvMaxHeight: number | string = '2rem';
  @Output() lvScrollEnd = new EventEmitter<any>(); // 虚拟滚动到底部时回调事件
  @Input() @InputBoolean() lvVirtualScroll = false; // 是否使用虚拟滚动（大数据场景使用）
  @Input() @InputNumber() lvVirtualMaxBuffer = 200; // 渲染新项时最大缓冲高度
  @Input() @InputNumber() lvVirtualMinBuffer = 100; // 视图之外渲染的最小缓冲区数，若缓存区数量低于此数量将渲染新项
  @Output() lvInputChange = new EventEmitter<any>(); // 输入变化回调
  @Output() lvScrollingChange = new EventEmitter<any>(); // 虚拟滚动中回调事件

  @Input() lvData: string[] | MultiAutocompleteItem[] = [];
  @ViewChild('resizeHandle') resizeHandle: ElementRef<Element>;
  @ViewChild('multiAutocomplete') multiAutocomplete: ElementRef<any>;

  @ViewChild('inputArea', { static: false }) inputArea: ElementRef<any>;
  @ViewChild('multiAutocompleteLint', { static: false }) inputLint: ElementRef<any>;
  @ViewChild('autocomplete', { static: false }) autocomplete: AutocompleteDirective;
  @Input() lvScroll: { x?: string | null; y?: string | null } = {
    y: '200px',
  }; // 下拉选项水平垂直高度，超过滚动 目前只有y

  @Input() lvPosition: 'topLeft' | 'topCenter' | 'topRight' | 'bottomLeft' | 'bottomCenter' | 'bottomRight' =
    'bottomLeft';

  maxHeight;
  value = ''; // 输入框的内容
  selectedData = []; // 组件选中的内容
  _panelRect: DOMRect;

  _isMouseDown = false; // 鼠标是否按下
  _initMaxHeight: number; // 容器初始最大高度
  tagData: TagItem[] = []; // tag选中的内容
  _panelContent: HTMLElement;

  private isInComposition: boolean = false; // 是否拼音输入中
  private preValue: string; // 中文输入前的值

  constructor(
    public cdr: ChangeDetectorRef,
    public el: ElementRef,
    private renderer: Renderer2,
    private resizeObserver: ResizeObserverService,
  ) {}

  get inputWidth(): number {
    return this.inputLint.nativeElement.clientWidth + 10;
  }

  get inputEmpty(): boolean {
    return this.inputArea && this.inputArea.nativeElement.value === '';
  }

  // 空
  get dataEmpty(): boolean {
    // 是否有选中项, null undefined 空数组
    return _isNil(this.selectedData) || (this.selectedData && !this.selectedData.length);
  }

  @HostListener('document:mouseup')
  onMouseUp(): void {
    this._isMouseDown = false;
  }

  @HostListener('document:mousedown', ['$event'])
  onMouseDown(event: MouseEvent): void {
    this.resizeDown(event);
  }

  @HostListener('document:mousemove', ['$event'])
  onMouseMove(event: MouseEvent): void {
    this.resizePanel(event);
  }

  public _onModelChange: any = (): void => {};

  registerOnTouched(): void {}

  onCompositionstart(): void {
    // 当value已经达到限制字数时，中文输入结束后，value不会变化，因此不需要触发inputChange，所以此处添加preValue用来做判断
    this.preValue = this.value;
    this.isInComposition = true;
  }

  onCompositionend(): void {
    this.isInComposition = false;
  }

  // Autocomplete 输入值发生改变执行事件
  inputChange(event): void {
    // 中文输入结束且值变更了再去触发lvInputChange
    if (!this.isInComposition && this.preValue !== this.value) {
      this.preValue = null;
      this.lvInputChange.emit(event);
    }
  }

  // 点击输入控件自动聚焦到输入框
  _focusInput(): void {
    this.inputArea.nativeElement.focus();
  }

  registerOnChange(fn: any): void {
    this._onModelChange = fn;
  }

  // tag 清除所有回调
  clearAllTags(): void {
    this.selectedData = [];
    this._onModelChange(this.selectedData);
  }

  writeValue(value: any): void {
    this.selectedData = value || [];
    this.initTagData();
    this.cdr.markForCheck();
  }

  // tag 清除单个回调
  removeTag(event): void {
    const tagIndex = this.selectedData.findIndex((item): boolean => event.label === (item.value || item));
    this.selectedData.splice(tagIndex, 1);
    this._onModelChange(this.selectedData);
  }

  _changeClass(type): void {
    if (type) {
      this.renderer.addClass(this.el.nativeElement, 'lv-multi-autocomplete-active');
    } else {
      this.renderer.removeClass(this.el.nativeElement, 'lv-multi-autocomplete-active');
    }
  }

  ngOnInit(): void {
    // 输入超长换行时，自动更新弹层的位置
    this.resizeObserver.observe(this.el.nativeElement, (): void => {
      const overlay = this.autocomplete.autocomplete._overlay;
      if (overlay?.overlayRef.hasAttached()) {
        overlay.overlayRef.updatePosition();
      }
    });
  }

  initTagData(): void {
    this.tagData = [];
    if (this.selectedData) {
      this.selectedData.forEach((element): void => {
        this.tagData.push({
          label: element.value || element,
          removeable: true,
          disabled: this.lvDisabled,
          /** @deprecated: 平滑过渡，使用disabled替代，后续删除 */
          disabeld: this.lvDisabled,
        });
      });
    }
  }

  resizeDown(event: MouseEvent): void {
    const scalable = this.resizeHandle?.nativeElement === event.target && !this.lvDisabled && this.lvResize;
    if (!scalable) {
      return;
    }
    this._panelContent = this.multiAutocomplete.nativeElement;
    this._panelRect = this._panelContent.getBoundingClientRect(); // 获取panel面板宽高
    this._isMouseDown = true; // 鼠标按下状态设置为true
  }

  ngOnChanges(changes: SimpleChanges): void {
    if (changes.lvDisabled) {
      // 禁用时，所有的tag值都不能进行操作
      if (this.tagData.length) {
        this.tagData.forEach((tag): void => {
          tag.disabled = changes.lvDisabled.currentValue;
          tag.disabeld = changes.lvDisabled.currentValue;
        });
      }
      this.tagData = [...this.tagData];
    }

    if (changes.lvMaxHeight) {
      this.maxHeight = typeConvert(changes.lvMaxHeight.currentValue);
    }
  }

  // Autocomplete 选中值后执行事件
  selectedChange(event): void {
    const fn = (): void => {
      this.tagData.push({
        label: event.value || event,
        removeable: true,
      });
      this.selectedData.push(event);
      this._onModelChange(this.selectedData);
      this._inputReset();
    };

    if (!this.lvBeforeValueChange) {
      fn();
    } else {
      const call = this.lvBeforeValueChange(event);
      doFnResult(call, (result): void => {
        if (result) {
          fn();
        } else {
          this._inputReset();
        }
      });
    }
  }

  // 回车事件
  enterEvent(event): void {
    if (this.lvAllowNewItem && event !== '') {
      const fn = (): void => {
        this.tagData.push({
          label: event,
          removeable: true,
        });
        this.selectedData.push(event);
        this._onModelChange(this.selectedData);
        this._inputReset();
      };

      if (!this.lvBeforeValueChange) {
        fn();
      } else {
        const call = this.lvBeforeValueChange(event);
        doFnResult(call, (result): void => {
          if (result) {
            fn();
          } else {
            this._inputReset();
          }
        });
      }
    }
  }

  /**
   * 改变panel宽高
   */
  resizePanel(event: MouseEvent): boolean {
    // 如果鼠标是按下状态
    if (this._isMouseDown) {
      const screenHeight = window.innerHeight;
      const MAX = screenHeight - this._panelRect.top;
      const MIN = 32;

      // 获取元素初始的maxHeight;
      const _maxHeight = getComputedStyle(this._panelContent, null).maxHeight;
      this._initMaxHeight = this._initMaxHeight || parseInt(_maxHeight, 10);

      const getActualSize = (param): any => {
        if (param > MAX) {
          return MAX;
        } else if (param < MIN) {
          return MIN;
        } else {
          return param;
        }
      };
      const panelHeight = event.y - this._panelRect.top + 5; // 计算panel元素高度，5为拖拽handler的高度
      const maxHeight = panelHeight > this._initMaxHeight ? panelHeight : this._initMaxHeight;
      // 先设置maxHeight，再更改height
      this._panelContent.style.maxHeight = `${maxHeight}px`;
      this._panelContent.style.height = `${getActualSize(panelHeight)}px`;
    }
    return false; // 必须返回false，否则会触发系统事件，导致功能异常
  }

  /**
   * 选中/输入合适选项后，重置输入框
   */
  private _inputReset(): void {
    this.value = '';
    this.inputLint.nativeElement.style.width = 0;
    // 置空输入框
    const delay = setTimeout((): void => {
      this.inputArea.nativeElement.value = '';
      this.inputLint.nativeElement.style.width = 'auto';
      clearTimeout(delay);
    }, 0);
  }
}
